package com.qianfeng.smartdevice.logs;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qianfeng.smartdevice.execption.MyBaseException;
import com.qianfeng.smartdevice.logs.annotation.LogAnnotation;
import com.qianfeng.smartdevice.pojo.SysOperLog;
import com.qianfeng.smartdevice.pojo.SysUserInfo;
import com.qianfeng.smartdevice.utils.RequestUtil;
import com.qianfeng.smartdevice.utils.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.concurrent.CompletableFuture;

/**
 * Created by wenka on 2022/3/28 22:55
 *
 * @author wenkaixin
 * @version 1.0
 * @since 1.0
 */
@Aspect
@Controller
public class LogAdvice {
    private ObjectMapper objectMapper;

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    //我们要在请求地址后出事情
    //定义切入点表达式
    //拦截带有这个主键的方法
    @Pointcut("@annotation(com.qianfeng.smartdevice.logs.annotation.LogAnnotation)")
    public void pointcut() {
    }

    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {

        //获取数据，进行处理
        //获取请求参数相关的数据
        //获取到返回结果的json
        //记录请求的方式，地址时间，参数等值
        //将数据保存到数据库
        SysOperLog sysOperLog = new SysOperLog();
        try {
            sysOperLog.setErrorMsg("200,OK");
            sysOperLog.setJsonResult(objectMapper.writeValueAsString(result));
            //设置为成功，因为凡是出现问题的操作都被抛异常抛出去了，不会正常结束，所以等会不要忘记还要写一场的aop
            sysOperLog.setStatus(1L);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        addLog(joinPoint, sysOperLog);
    }

    @AfterThrowing(pointcut = "pointcut()", throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
        SysOperLog sysOperLog = new SysOperLog();
        try {
            //设置错误信息
            sysOperLog.setErrorMsg(throwable.getMessage());
            if (throwable instanceof MyBaseException) {
                sysOperLog.setStatus((long) ((MyBaseException) throwable).getCode());
            } else {
                sysOperLog.setStatus(0L);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        addLog(joinPoint, sysOperLog);
    }

    public void addLog(JoinPoint joinPoint, SysOperLog sysOperLog) {
        sysOperLog.setOperTime(new Date());
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        SysUserInfo loginUser = SecurityUtils.getLoginUser();
        CompletableFuture.runAsync(() -> {
            //获取请求地址
            String requestURI = request.getRequestURI().toString();
            //请求方式
            String requestmethod = request.getMethod();
            //获取IP
            String ip = RequestUtil.getRemoteHost(request);
            String locationByIp = RequestUtil.getLocationByIp(ip);
            sysOperLog.setMethod(requestmethod);
            sysOperLog.setOperIp(ip);
            sysOperLog.setOperLocation(locationByIp);
            sysOperLog.setOperUrl(requestURI);
            if ((loginUser == null)) {
                sysOperLog.setOperatorType(0L);
                sysOperLog.setOperName("");
            } else {
                sysOperLog.setOperName(loginUser.getUsername());
                //实际情况可以根据我们要求传递的某个头区分
                sysOperLog.setOperatorType(2L);
            }
            //获取那些比较麻烦的数据，比如请求的参数，执行的方法名，还有一些注解的数据
            //获取到当前正在拦截的方法的签名
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //正在拦截的那个方法
            Method method = methodSignature.getMethod();
            //正在被拦截的方法的名字
            String methodSignatureName = methodSignature.getName();
            sysOperLog.setMethod(methodSignatureName);
            //当前方法执行的时候传递的实际的参数
            Object[] args = joinPoint.getArgs();
            try {
                //参数设置好了
                sysOperLog.setOperParam(objectMapper.writeValueAsString(args));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            //获取当前正在拦截的方法上面的注解
            LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
            String title = logAnnotation.title();
            int businessType = logAnnotation.businessType();
            sysOperLog.setTitle(title);
            sysOperLog.setBusinessType((long) businessType);
            System.out.println(sysOperLog);

        });

    }
}
